package be.rivendale.mathematics;

import org.junit.Test;

import static be.rivendale.mathematics.MathematicalAssert.*;

public class LineTest {
    @Test
    public void constructorSetsPointACorrectly() throws Exception {
        Line line = new Line(new Triple(0, 1, 2), new Triple(3, 4, 5));
        assertPointEquals(new Triple(0, 1, 2), line.getA());
    }

    @Test
    public void constructorSetsPointBCorrectly() throws Exception {
        Line line = new Line(new Triple(0, 1, 2), new Triple(3, 4, 5));
        assertPointEquals(new Triple(3, 4, 5), line.getB());
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenTheTwoPointsAreEqual() throws Exception {
        new Line(new Triple(1, 2, 3), new Triple(1, 2, 3));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointAIsNull() throws Exception {
        new Line(null, new Triple(3, 4, 5));
    }

    @Test(expected = IllegalArgumentException.class)
    public void constructorThrowsIllegalArgumentExceptionWhenPointBIsNull() throws Exception {
        new Line(new Triple(0, 1, 2), null);
    }

    @Test
    public void slopeReturnTheDirectionVectorOfThisLine() throws Exception {
        Line line = new Line(new Triple(1, 2, 3), new Triple(5, -3, 5));
        assertVectorEquals(new Triple(4, -5, 2), line.direction());
    }

    @Test
    public void pointOnLineReturnsPointAWhenTheParameterEqualsZero() throws Exception {
        Line line = new Line(new Triple(1, 2, 3), new Triple(5, -3, 5));
        Point point = line.pointOnLine(0);
        assertPointEquals(new Triple(1, 2, 3), point);
    }

    @Test
    public void pointOnLineReturnsPointBWhenTheParameterEqualsOne() throws Exception {
        Line line = new Line(new Triple(1, 2, 3), new Triple(5, -3, 5));
        Point point = line.pointOnLine(1);
        assertPointEquals(new Triple(5, -3, 5), point);
    }

    @Test
    public void pointOnLineReturnsPointOnTheLine() throws Exception {
        Line line = new Line(new Triple(1, 2, 3), new Triple(5, -3, 5));
        Point point = line.pointOnLine(-0.5);
        assertPointEquals(new Triple(-1, 4.5, 2), point);
    }
	
	@Test
	public void distanceToLineReturnsClosestDistanceToSpecifiedLineCaseOne() throws Exception {
		Line a = new Line(new Vertex(1, 2, 2), new Vertex(5, 5, 4));
		Line b = new Line(new Vertex(1, 0, -3), new Vertex(5, -6, -4));
		assertRealEquals(4, a.distance(b));
	}

	@Test
	public void distanceToLineReturnsClosestDistanceToSpecifiedLineCaseTwo() {
	    Line a = new Line(new Triple(1, 2, 3), new Triple(1, -1, 2));
		Line b = new Line(new Triple(5, 4, 0), new Triple(3, 4, 2));
		assertRealEquals(0.229416, a.distance(b));
	}

	@Test(expected = IllegalArgumentException.class)
	public void distanceToLineThrowsIllegalArgumentExceptionWhenSpecifiedLineIsNull() throws Exception {
		new Line(new Vertex(1, 2, 2), new Vertex(5, 5, 4)).distance((Line) null);
	}

	@Test
	public void distanceToLineReturnsDistanceToTheSpecifiedLineIfBothLinesAreParallel() {
		Line a = new Line(new Triple(5, 4, 0), new Triple(3, 4, 2));
		Line b = new Line(new Triple(6, 5, 1), new Triple(4, 5, 3));
		assertRealEquals(1.732051, a.distance(b));
	}

	@Test
	public void distanceToLineReturnsZeroIfBothLinesAreEqual() {
		Line a = new Line(new Triple(6, 5, 1), new Triple(4, 5, 3));
		Line b = new Line(new Triple(6, 5, 1), new Triple(4, 5, 3));
		assertRealEquals(0, a.distance(b));
	}

	@Test
	public void distanceToLineReturnsZeroIfBothLinesCoincideButHaveDifferentPoints() {
		Line a = new Line(new Triple(0, 1, 0), new Triple(0, 2, 0));
		Line b = new Line(new Triple(0, -1, 0), new Triple(0, -2, 0));
		assertRealEquals(0, a.distance(b));
	}
	
	@Test
	public void distanceBetweenLinesReturnsZeroIfBothLinesIntersect() {
		Line a = new Line(new Vertex(-1, 1, -1), new Vertex(1, -1, 1));
		Line b = new Line(new Triple(-1, -1, 1), new Triple(1, 1, -1));
		assertRealEquals(0, a.distance(b));
	}

	@Test(expected = IllegalArgumentException.class)
	public void distanceToPointRequiresAPoint() {
	    new Line(new Triple(0, 1, 2), new Triple(1, -1, 1)).distance((Point) null);
	}

	@Test
	public void distanceToPointReturnsTheCorrectDistanceToThePoint() {
		double actualDistance = new Line(new Triple(0, 1, 2), new Triple(1, -1, 1)).distance(new Triple(8, 2, 3));
		assertRealEquals(7.863417, actualDistance);
	}
}
